﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Configuration;
using System.Web.SessionState;
using OBiCMS.Dean.Edwards;

//    Copyright (C) 2010 Officebroker.com LTD.
//    Neil Cadman, Alex Welding, Andrew Coombs, Jan Rygielski
//    Sir Robert Peel Mill, Mill Lane, Staffordshire, Fazely, B78 3QD, England
//
//    This file is part of OBiCMSFramework.
//
//    OBiCMSFramework is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    OBiCMSFramework is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License (GNU AGPL)
//    along with OBiCMSFramework.  If not, see <http://www.gnu.org/licenses/>.

namespace OBiCMSFramework
{
    internal class OBiHTTPHandler : IHttpHandlerFactory, IRequiresSessionState
    {
        // Private Variables
        static private Boolean _loaded = false;
        static private List<URLRewriteJob> _urlRewriteJobs = new List<URLRewriteJob>();

        // Public Methods
        public IHttpHandler GetHandler(HttpContext context, String requestType, String url, String pathTranslated)
        {
            Stopwatch timer = new Stopwatch();
            String foundCMSPageID = "";

            if (context.Request.Cookies["UseDiagnostics"] == null)
            {
                HttpCookie diagCookie = new HttpCookie("UseDiagnostics");
                diagCookie.Value = "false";
                diagCookie.Expires = DateTime.Now.AddDays(365);
                context.Response.Cookies.Add(diagCookie);
            }

            if (!_loaded)
            {
                Configuration config = WebConfigurationManager.OpenWebConfiguration("/");
                if (config.AppSettings.Settings["JavascriptPackEncoding"] != null && config.AppSettings.Settings["JavascriptPackFastDecode"] != null && config.AppSettings.Settings["JavascriptPackSpecialChars"] != null)
                {
                    String encoding = config.AppSettings.Settings["JavascriptPackEncoding"].Value;
                    ECMAScriptPacker.PackerEncoding encodingValue;

                    switch (encoding.ToLower())
                    {
                        case "none":
                            encodingValue = ECMAScriptPacker.PackerEncoding.None;
                            break;
                        case "numeric":
                            encodingValue = ECMAScriptPacker.PackerEncoding.Numeric;
                            break;
                        case "mid":
                            encodingValue = ECMAScriptPacker.PackerEncoding.Mid;
                            break;
                        case "normal":
                            encodingValue = ECMAScriptPacker.PackerEncoding.Normal;
                            break;
                        case "highascii":
                            encodingValue = ECMAScriptPacker.PackerEncoding.HighAscii;
                            break;
                        default:
                            encodingValue = ECMAScriptPacker.PackerEncoding.None;
                            break;
                    }

                    StaticObjects.PackJavascript = new StaticObjects.javascriptPack(true, encodingValue, (config.AppSettings.Settings["JavascriptPackFastDecode"].Value == "true" ? true : false), (config.AppSettings.Settings["JavascriptPackSpecialChars"].Value == "true" ? true : false));
                }
                else
                    StaticObjects.PackJavascript = new StaticObjects.javascriptPack(false, ECMAScriptPacker.PackerEncoding.None, false, false);

                _loaded = true;
            }

            // Start the Speed Test
            if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                timer.Start();

            // Check to see if the handler should be processing requests.
            if (!StaticObjects.HTTPhandlerLive)
            {
                if (context.Request.RawUrl.ToString().ToLower().StartsWith("/obicmsframeworkadmin.cmsjob"))
                    return new AdminPage();

                context.Response.WriteFile(context.Server.MapPath(context.Request.Url.LocalPath.ToString()));
                return new StandardPage();
            }

            // Do we have any connection strings to use?
            if (StaticObjects.ConnectionStrings.Count == 0)
            {
                Configuration config = WebConfigurationManager.OpenWebConfiguration("/");
                foreach (ConnectionStringSettings connString in config.ConnectionStrings.ConnectionStrings)
                    StaticObjects.ConnectionStrings.Add(connString.ConnectionString);

                // If we still have no connection string return an error.
                if (StaticObjects.ConnectionStrings.Count == 0)
                    return new ErrorPage(new CMSException("No connection strings have been set in the web.config"));
            }

            // If this is the first time the CMS has ran load up the URLRewriteJobs and objects from saved cache.
            if (StaticObjects.GetURLRewriteJobs)
            {
                getURLRewriteJobs();
                StaticObjects.GetURLRewriteJobs = false;
            }

            if (String.IsNullOrEmpty(StaticObjects.SavedStatesLocation))
                StaticObjects.SavedStatesLocation = context.Server.MapPath("SavedState/");

            // Check if this is a Javascript or Css request.
            // Checks also if it is a OBiCMS Javascript or Css.
            String virtualURL = context.Request.Path.ToString().ToLower();
            if (virtualURL.EndsWith(".js"))
            {
                if (context.Request.QueryString["OBiVersion"] != null)
                {
                    if (context.Request.QueryString["OBiVersion"].ToString().ToLower() == "dynamic")
                    {
                        String fileName = System.IO.Path.GetFileName(virtualURL);

                        if (context.Request.QueryString["Defered"] != null)
                        {
                            if (context.Request.QueryString["Defered"].ToString().ToLower() == "false")
                                return new JavaScriptPage((String)BasePage.CachedJavaScript[fileName + "_javascript"], timer.ElapsedTicks);
                            else if (context.Request.QueryString["Defered"].ToString().ToLower() == "true")
                                return new JavaScriptPage((String)BasePage.CachedJavaScript[fileName + "_javascriptdefered"], timer.ElapsedTicks);
                        }
                        else
                            return new JavaScriptPage((String)BasePage.CachedJavaScript[fileName + "_javascript"], timer.ElapsedTicks);

                    }
                }
                else
                    context.Response.ContentType = "application/x-javascript";
            }
            else if (virtualURL.EndsWith(".css"))
                if (context.Request.QueryString.AllKeys.Contains("OBiVersion"))
                {
                    if (context.Request.QueryString["OBiVersion"].ToString().ToLower() == "dynamic")
                    {
                        String fileName = System.IO.Path.GetFileName(virtualURL);
                        fileName = fileName.Replace(".css", "");
                        foreach (BasePage page in BasePage.CachedBasePages)
                            if (page.ID.ToLower() == fileName.ToLower())
                                return new CSSPage(page.CSSSnippets[context.Request.QueryString["Type"].ToString()], timer.ElapsedTicks);
                    }
                }
                else
                    context.Response.ContentType = "text/css";

            // Does this request match one of our jobs.
            foreach (URLRewriteJob urlRewriteJob in _urlRewriteJobs)
            {
                if (urlRewriteJob.Pattern.IsMatch(context.Request.RawUrl))
                {
                    String originalQueryString = context.Request.QueryString.ToString();
                    String constructedQueryString = urlRewriteJob.getQueryString(context.Request.RawUrl.Split("?".ToCharArray())[0]);

                    foundCMSPageID = urlRewriteJob.CMSPageID;
                    context.RewritePath(context.Request.Url.AbsolutePath.ToString(), "", constructedQueryString + (constructedQueryString == "" ? "" : "&") + originalQueryString, false);
                    break;
                }
            }

            // Did we find a page in the CMS for this request.
            if (String.IsNullOrEmpty(foundCMSPageID))
            {

                // If the page type is of aspx or asp then we simply return the compiled code.
                if (context.Request.Url.LocalPath.ToString().ToLower().EndsWith(".aspx") || context.Request.Url.LocalPath.ToString().ToLower().EndsWith(".asp"))
                    return new AspPage(url);

                // If the job is an admin task, then get the server to attempt to load and return the requested URL.
                // If the page does not exists or there is any other sort of error the server will return error messages
                // in the default way, 404, 500 etc.
                if (context.Request.RawUrl.ToString().ToLower().StartsWith("/obicmsframeworkadmin.cmsjob"))
                {
                    String username = (context.Request.QueryString["UserName"] == null ? "" : context.Request.QueryString["UserName"].ToString());
                    String password = (context.Request.QueryString["Password"] == null ? "" : context.Request.QueryString["Password"].ToString());
                    Configuration config = WebConfigurationManager.OpenWebConfiguration("/");

                    if (username == config.AppSettings.Settings["CMSFrameWorkUserName"].Value.ToString() &&
                        password == config.AppSettings.Settings["CMSFrameWorkPassword"].Value.ToString())
                        return new AdminPage();
                }

                context.Response.WriteFile(context.Server.MapPath(context.Request.Url.LocalPath.ToString()));
                return new StandardPage();
            }
            else
            {
                // The handler will remove the BasePage which gets it settings passed in from a session
                if (context.Request.RawUrl.ToString().ToLower().StartsWith("/cmspreview.aspx"))
                {
                    if (BasePage.CachedBasePagesHash.Contains(foundCMSPageID))
                    {
                        BasePage.CachedBasePages.Remove(BasePage.getBasePage(foundCMSPageID));
                        BasePage.CachedBasePagesHash.Remove(foundCMSPageID);
                    }

                }
                // The handler will then return our BasePage.
                    return new MainPage(BasePage.getBasePage(foundCMSPageID), timer);
            }
        }
        public void ReleaseHandler(IHttpHandler handler)
        {
            handler = null;
        }

        // Private Methods
        private void getURLRewriteJobs()
        {
            DataSet ds;
            List<URLRewriteJob> rewriteJobs = new List<URLRewriteJob>();
            String sql = "select * from getURLRewriteJobs()";

            ds = StaticObjects.GetDataset(sql);

            if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
                return;

            foreach (DataRow dr in ds.Tables[0].Rows)
                rewriteJobs.Add(new URLRewriteJob(dr["CMSBasePageID"].ToString(),
                                                  new Regex(dr["Pattern"].ToString(), RegexOptions.IgnoreCase),
                                                  dr["QueryString"].ToString()));

            _urlRewriteJobs = rewriteJobs;
        }

        // Public Classes
        internal class CMSException : Exception
        {
            // Private Variables
            private Dictionary<String, String> _debugItems = new Dictionary<String, String>();
            private String _message = "";

            // Public Properties
            public Dictionary<String, String> DebugItems
            {
                get { return _debugItems; }
            }
            public override String Message
            {
                get { return _message; }
            }

            // Public Constructors
            public CMSException(String message)
                : this(message, null, null)
            { }
            public CMSException(String message, Dictionary<String, String> debugItems)
                : this(message, null, debugItems)
            { }
            public CMSException(String message, Exception innerExecption)
                : this(message, innerExecption, null)
            { }
            public CMSException(String message, Exception innerExecption, Dictionary<String, String> debugItems)
                : base(message, innerExecption)
            {
                if (debugItems != null)
                {
                    message += "Debug Items :" + Environment.NewLine;
                    foreach (KeyValuePair<String, String> item in debugItems)
                        message += item.Key + " :- " + Environment.NewLine + item.Value + Environment.NewLine;
                }

                _message = message;
            }
        }

        // Private Classes
        private class MainPage : IHttpHandler, IRequiresSessionState
        {
            // Private Variables
            private BasePage _page;
            private Stopwatch _timer = new Stopwatch();

            // Public Methods
            public MainPage(BasePage page, Stopwatch timer)
            {
                _page = page;
                _timer = timer;
            }
            public virtual void ProcessRequest(HttpContext context)
            {
                context.Session["OBiCMSFrameworkLastRequest$$"] = DateTime.UtcNow;

                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                    context.Response.Write("<!--Start Process Time : " + _timer.ElapsedTicks + "-->" + Environment.NewLine);

                if (!_page.Online)
                    context.Response.StatusCode = 404;
                else
                {
                    if (context.Session["$$CMSPREVIEWSETTINGS$$"] != null)
                    {
                        try
                        {
                            BasePage.SetXMLSettings(_page, context.Session["$$CMSPREVIEWSETTINGS$$"].ToString().Replace("[CMSQM]", "?").Replace("[CMSLTS]", "<"));
                        }
                        catch (CMSException e)
                        {
                            ErrorPage cmsError = new ErrorPage(e);
                            cmsError.ProcessRequest(context);
                            context.Session.Remove("$$CMSPREVIEWSETTINGS$$");
                            return;
                        }
                        catch (Exception e2)
                        {
                            ErrorPage cmsError = new ErrorPage(new CMSException("Could not load page preview", e2));
                            cmsError.ProcessRequest(context);
                            context.Session.Remove("$$CMSPREVIEWSETTINGS$$");
                            return;
                        }

                        context.Session.Remove("$$CMSPREVIEWSETTINGS$$");
                        
                    }

                    BasePage.PageContent content = _page.GeneratePage(context, _timer, (context.Request.Cookies["UseDiagnostics"].Value == "true" ? true : false));
                    
                    context.Response.Write(content.Body);

                    if (context.Request.Headers["If-Modified-Since"] != null && DateTime.Parse(context.Request.Headers["If-Modified-Since"]) >= content.LastChanged)
                        context.Response.StatusCode = 304;
                    else
                        context.Response.StatusCode = 200;
                }

                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                    context.Response.Write("<!--End Process Time : " + (_timer.ElapsedTicks) + "-->");

                _timer.Stop();
            }
            public virtual bool IsReusable
            {
                get { return false; }
            }

        }
        private class AspPage : IHttpHandler, IRequiresSessionState
        {
            // Private Variables
            private String _url;

            // Public Methods
            public AspPage(String url)
            {
                _url = url;
            }
            public virtual void ProcessRequest(HttpContext context)
            {
                IHttpHandler temp = System.Web.UI.PageParser.GetCompiledPageInstance(_url, context.Server.MapPath(context.Request.Url.LocalPath.ToString()), context);
                temp.ProcessRequest(context);
            }
            public virtual bool IsReusable
            {
                get { return false; }
            }
        }
        private class JavaScriptPage : IHttpHandler, IRequiresSessionState
        {
            // Private Variables
            private Stopwatch timer = new Stopwatch();
            private Int64 _startTime;
            private String _content = "";

            // Public Constructors
            public JavaScriptPage(String content, Int64 startTime)
            {
                _startTime = startTime;
                _content = content;
            }

            // Public Methods
            public virtual void ProcessRequest(HttpContext context)
            {
                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                {
                    context.Response.Write("// Start Process Time : " + _startTime + " //" + Environment.NewLine);
                    timer.Start();
                }

                context.Response.ContentType = "application/x-javascript";
                context.Response.Write(_content);

                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                {
                    timer.Stop();
                    context.Response.Write("// End Process Time : " + (timer.ElapsedTicks + _startTime) + " //");
                }
            }
            public virtual bool IsReusable
            {
                get { return false; }
            }

        }
        private class CSSPage : IHttpHandler, IRequiresSessionState
        {
            // Private Variables
            private Stopwatch timer = new Stopwatch();
            private Int64 _startTime;
            private List<BasePage.CSSSnippet> _content = new List<BasePage.CSSSnippet>();

            // Public Methods
            public CSSPage(List<BasePage.CSSSnippet> content, Int64 startTime)
            {
                _startTime = startTime;
                _content = content;
            }
            public virtual void ProcessRequest(HttpContext context)
            {
                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                {
                    context.Response.Write("/* Start Process Time : " + _startTime + " */" + Environment.NewLine);
                    timer.Start();
                }

                context.Response.ContentType = "text/css";

                foreach (BasePage.CSSSnippet cssSnippet in _content)
                {
                    if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                    {
                        context.Response.Write("/*  SnippetID : " + cssSnippet.ID + Environment.NewLine);
                        context.Response.Write("    Version : " + cssSnippet.Version + Environment.NewLine);
                        context.Response.Write("    Generated at : " + cssSnippet.DateChanged + " */" + Environment.NewLine);
                    }
                    context.Response.Write(cssSnippet.Body + Environment.NewLine);
                }

                if (context.Request.Cookies["UseDiagnostics"].Value == "true")
                {
                    timer.Stop();
                    context.Response.Write("/* End Process Time : " + (timer.ElapsedTicks + _startTime) + " */");
                }
            }
            public virtual bool IsReusable
            {
                get { return false; }
            }

        }
        private class StandardPage : IHttpHandler, IRequiresSessionState
        {
            // Public Methods
            public StandardPage()
            {
            }
            public virtual void ProcessRequest(HttpContext context)
            {
            }
            public virtual bool IsReusable
            {
                get { return false; }
            }

        }
        private class ErrorPage : IHttpHandler, IRequiresSessionState
        {
            // Private Variables
            private CMSException _e;

            public ErrorPage(CMSException e)
            {
                _e = e;
            }

            // Public Methods
            public virtual void ProcessRequest(HttpContext context)
            {
                context.Response.Write(GenerateBody());
            }
            public virtual bool IsReusable
            {
                get { return true; }
            }
            public String GenerateBody()
            {
                String errorMessage = "";
                Exception e;

                errorMessage += "CMS Error...<br/><br/>";
                errorMessage += "Message : " + _e.Message + "<br/>";
                errorMessage += "Source : " + _e.Source + "<br/>";
                errorMessage += "Trace : " + _e.StackTrace + "<br/><br/>";
                e = _e.InnerException;

                while (e != null)
                {
                    errorMessage += "Inner Exception :<br/>";
                    errorMessage += "Inner Exception Message : " + e.Message + "<br/>";
                    errorMessage += "Inner Exception Source : " + e.Source + "<br/>";
                    errorMessage += "Inner Exception Trace : " + e.StackTrace + "<br/>";
                    e = e.InnerException;
                }

                return errorMessage;
            }

        }
        private class AdminPage : IHttpHandler, IRequiresSessionState
        {

            public AdminPage()
            { }

            // Public Methods
            public virtual void ProcessRequest(HttpContext context)
            {

                StringBuilder output = new StringBuilder();

                // Create HTML for the page.
                output.Append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" + Environment.NewLine);
                output.Append("<!-- Page Generated by OBiCMSFramework -->" + Environment.NewLine);
                output.Append("<HTML>" + Environment.NewLine);
                output.Append("<head>" + Environment.NewLine);
                output.Append("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />" + Environment.NewLine);
                output.Append("<title>Admin Response </title>" + Environment.NewLine);
                output.Append("</head>" + Environment.NewLine);
                output.Append("<body>" + Environment.NewLine);

                output.Append(adminTaskCheck(context));

                output.Append("</body>" + Environment.NewLine);
                output.Append("</html>" + Environment.NewLine);

                context.Response.Write(output.ToString());

            }
            public virtual bool IsReusable
            {
                get { return true; }
            }
            private String adminTaskCheck(HttpContext context)
            {
                String url = context.Request.RawUrl.ToString().ToLower();
                String jobType = (context.Request.QueryString["job"] == null ? "" : context.Request.QueryString["job"].ToString());
                
                switch (jobType.ToLower())
                {
                    case "clear-saved-state":
                        ClearSnapShots(StaticObjects.SavedStatesLocation);
                        return "Saved state cleared";
                    case "save-state":
                        String saveToPath = StaticObjects.SavedStatesLocation;

                        ClearSnapShots(saveToPath);

                        for (int i = 0; i < Template.CachedTemplates.Count; i++)
                            StaticObjects.Serialize(Template.CachedTemplates[i], Template.CachedTemplates[i].ID + ".template", saveToPath);

                        for (int i = 0; i < BasePage.CachedWidgets.Count; i++)
                            StaticObjects.Serialize(BasePage.CachedWidgets[i], BasePage.CachedWidgets[i].ID + ".widget", saveToPath);

                        for (int i = 0; i < BasePage.CachedCssSnippets.Count; i++)
                            StaticObjects.Serialize(BasePage.CachedCssSnippets[i], BasePage.CachedCssSnippets[i].ID + ".csssnippet", saveToPath);

                        return "State saved";
                    case "load-state":
                        lock (BasePage.CachedWidgets)
                            lock (Template.CachedTemplatesHash)
                                lock (BasePage.CachedCssSnippetsHash)
                                {
                                    ClearCache(context);
                                    StaticObjects.DeSerialize(System.IO.Directory.GetFiles(StaticObjects.SavedStatesLocation));
                                }

                        return "State restored";
                    case "cms-on":
                        StaticObjects.HTTPhandlerLive = true;
                        return "CMS is now on, all pages will get processed";
                    case "cms-off":
                        StaticObjects.HTTPhandlerLive = false;
                        return "CMS is now off, only admin tasks will be processed";
                    case "diag-on":
                        context.Response.Cookies["UseDiagnostics"].Value = "true";
                        return "Diag mode on";
                    case "diag-off":
                        context.Response.Cookies["UseDiagnostics"].Value = "false";
                        return "Diag mode off";
                    case "view-errorlog":
                        return StaticObjects.ErrorLog;
                    case "reload-urls":
                        StaticObjects.GetURLRewriteJobs = true;
                        return "URLs will be reloaded on next request";
                    case "view-cache":

                        StringBuilder cachedObjects = new StringBuilder();

                        cachedObjects.Append("Memory usage in bytes<br/>" + GC.GetTotalMemory(true).ToString() + "<br/><br/>");

                        cachedObjects.Append("<ul><li>Pages (" + BasePage.CachedBasePages.Count.ToString() + ") ...</li>");
                        for (int i = 0; i < BasePage.CachedBasePages.Count; i++)
                            cachedObjects.Append("<li>" + BasePage.CachedBasePages[i].ID + " : " + BasePage.CachedBasePages[i].DateChanged.ToString("yyyy/dd/MM HH:mm:ss") + "</li>");
                        cachedObjects.Append("</ul>");

                        cachedObjects.Append("<ul><li>Templates (" + Template.CachedTemplates.Count.ToString() + ") ...</li>");
                        for (int i = 0; i < Template.CachedTemplates.Count; i++)
                            cachedObjects.Append("<li>" + Template.CachedTemplates[i].ID + " : " + Template.CachedTemplates[i].DateChanged.ToString("yyyy/dd/MM HH:mm:ss") + "</li>");
                        cachedObjects.Append("</ul>");

                        cachedObjects.Append("<ul><li>Widgets (" + BasePage.CachedWidgets.Count.ToString() + ") ...</li>");
                        for (int i = 0; i < BasePage.CachedWidgets.Count; i++)
                            cachedObjects.Append("<li>" + BasePage.CachedWidgets[i].ID + " : " + BasePage.CachedWidgets[i].DateChanged.ToString("yyyy/dd/MM HH:mm:ss") + "</li>");
                        cachedObjects.Append("</ul>");

                        cachedObjects.Append("<ul><li>JavaScript (" + BasePage.CachedJavaScript.Count.ToString() + ") ...</li>");
                        lock (BasePage.CachedJavaScript)
                        {
                            foreach (String javaKey in BasePage.CachedJavaScript.Keys)
                                cachedObjects.Append("<li>" + javaKey + "</li>");
                        }
                        cachedObjects.Append("</ul>");

                        cachedObjects.Append("<ul><li>Css Snippets (" + BasePage.CachedCssSnippets.Count.ToString() + ") ...</li>");
                        for (int i = 0; i < BasePage.CachedCssSnippets.Count; i++)
                            cachedObjects.Append("<li>" + BasePage.CachedCssSnippets[i].ID + " : " + BasePage.CachedCssSnippets[i].DateChanged.ToString("yyyy/dd/MM HH:mm:ss") + "</li>");
                        cachedObjects.Append("</ul>");

                        return cachedObjects.ToString();
                    case "clear-cache":
                        ClearCache(context);

                        return "Cache clear";
                    default:
                        return null;
                }

            }

            private void ClearCache(HttpContext context)
            {
                Template[] oldTemplate = Template.CachedTemplates.ToArray();
                Template.CachedTemplates.Clear();
                Template.CachedTemplatesHash.Clear();
                foreach (Template template in oldTemplate)
                    template.StopAutoLoad();
                oldTemplate = null;

                BasePage.CSSSnippet[] oldCSSSnippets = BasePage.CachedCssSnippets.ToArray();
                BasePage.CachedCssSnippets.Clear();
                BasePage.CachedCssSnippetsHash.Clear();
                foreach (BasePage.CSSSnippet cssSnippet in oldCSSSnippets)
                    cssSnippet.StopAutoLoad();
                oldCSSSnippets = null;

                BasePage.CachedJavaScript.Clear();

                BasePage[] oldBasePages = BasePage.CachedBasePages.ToArray();
                BasePage.CachedBasePages.Clear();
                BasePage.CachedBasePagesHash.Clear();
                foreach (BasePage basePage in oldBasePages)
                    basePage.StopAutoLoad();
                oldBasePages = null;

                WidgetBase[] oldWidgets = BasePage.CachedWidgets.ToArray();
                BasePage.CachedWidgets.Clear();
                foreach (WidgetBase widget in oldWidgets)
                    widget.StopAutoLoad();
                oldWidgets = null;

                context.Application.Clear();
            }
            private void ClearSnapShots(String directory)
            {
                foreach (String file in Directory.GetFiles(directory))
                    File.Delete(file);
            }
        }

        // Private Structurs
        private struct URLRewriteJob
        {
            // Private Variabes
            private Regex _pattern;
            private String _cmsPageID;
            private String _replaceString;

            // Public Properties
            public Regex Pattern
            { get { return _pattern; } }
            public String CMSPageID
            { get { return _cmsPageID; } }
            public String ReplaceString
            { get { return _replaceString; } }

            // Public Constructors
            public URLRewriteJob(String cmsPageID, Regex pattern, String matchEvaluator)
            {
                _cmsPageID = cmsPageID;
                _pattern = pattern;
                _replaceString = matchEvaluator;
            }

            // Public Method
            public String getQueryString(String url)
            {
                return Pattern.Replace(url, _replaceString);
            }

        }

    }
}
